using System;
using System.Configuration;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Web.Services.Protocols;
using Microsoft.SharePoint;

// Project Server 2007 References
using Microsoft.Office.Project.Server.Library;
using Microsoft.Office.Project.Server.Events;

namespace Microsoft.EPM.Events.TimesheetEvent
{
    // Replace accordingly depending on what timesheet event handler you want to customize.
    // --> public class TSOnSubmitted : TimesheetEventReceiver
    public class TSOnUpdated : TimesheetEventReceiver
    {
        #region Properties
        // Initialize local parameters
        bool logEvents = false;
        bool logFileEveryHour = true;
        string logFolder;
        string sspName;
        bool submitStatus = false;
        EventEntry eventLog = new EventEntry();
        AppLog appLog;

        #endregion

        // --> public override void OnSubmitted(PSContextInfo contextInfo, TimesheetPostEventArgs e)
        public override void OnUpdated(PSContextInfo contextInfo, TimesheetPostEventArgs e)
        {
            // --> base.OnSubmitted(contextInfo, e);
            // Execute standard OnUpdated event
            base.OnUpdated(contextInfo, e);

            #region Local Variables

            string exceptionMsg = null;
            string tsInfo = string.Empty;
            Stopwatch timeToProcessAll = new Stopwatch();
            Stopwatch timeToProcessStatus = new Stopwatch();
            timeToProcessAll.Start();
            SPSite spSite = new SPSite(contextInfo.SiteGuid);
 
            #endregion

            try
            {
                #region STEP 0 - Load event handler settings from configuration file

                LoadSettings();

                if (logEvents)
                    appLog = new AppLog(spSite.HostName.ToUpper(), spSite.ServerRelativeUrl, logFolder, logFileEveryHour);

                #endregion

                #region STEP 1 - Initialize Web Services

                ResourceDerived resource = new ResourceDerived();
                StatusingDerived statusing = new StatusingDerived();
                TimesheetDerived timesheet = new TimesheetDerived();
                resource.Credentials = CredentialCache.DefaultCredentials;
                statusing.Credentials = CredentialCache.DefaultCredentials;
                timesheet.Credentials = CredentialCache.DefaultCredentials;
                statusing.Url = WSUrl(spSite, "statusing", true);
                resource.Url = WSUrl(spSite, "resource", false);
                timesheet.Url = WSUrl(spSite, "timesheet", false);

                #endregion

                #region STEP 2 - Retrieve Timesheet DataSet

                TimesheetWS.TimesheetDataSet timesheetDS = timesheet.ReadTimesheet(e.TsUID);
                TimesheetPSI tsHeader = new TimesheetPSI(timesheetDS);
                Guid tspUID = tsHeader.PeriodUID;
                Guid tresUID = tsHeader.ResUID;

                if (logEvents)
                {
                    appLog.WriteLine(string.Format("STEP 2 - Start event override for timesheet UID: {0}", e.TsUID.ToString()));
                    tsInfo = string.Format("Timesheet: {0}; Resource: {1}; Creator: {2}, Period: {3}",
                            tsHeader.Name, tsHeader.ResName, tsHeader.CreatorResName, tspUID.ToString());
                }
                #endregion

                #region STEP 3 - Perform statusing impersonation taking surrogate into account

                // Need to retrieve resource information to do proper impersonation
                ResourceWS.ResourceDataSet resourceDS = resource.ReadResource(tresUID);
                ResourcePSI resourcePSI = new ResourcePSI(resourceDS);

                // Check if surrogate timesheet
                if ((tsHeader.CreatorResUID != tsHeader.ResUID) // owner is different than creator
                    ||
                    (!resourcePSI.WindowsAccount.Equals(contextInfo.UserName))) // case where timesheet was modified by timesheet manager
                {
                    statusing.SetImpersonationContext(resourcePSI.IsWindowsUser, resourcePSI.WindowsAccount, tresUID, Guid.Empty, contextInfo.SiteGuid, contextInfo.Lcid);
                }
                else
                {
                    // Faster check that calling the resource web service
                    bool isWindowsUser = contextInfo.UserName.StartsWith("AspNetSqlMembershipProvider") ? false : true;
                    statusing.SetImpersonationContext(isWindowsUser, contextInfo.UserName, tresUID, Guid.Empty, contextInfo.SiteGuid, contextInfo.Lcid);
                }

                if (logEvents)
                    appLog.WriteLine(string.Format("STEP 3 - Statusing Impersonation for RES_UID: {0}", tresUID.ToString()));

                #endregion

                #region STEP 4 - Import Timesheet, will get an error if TS period is not within the Task's Start & Finish

                if (tspUID != Guid.Empty)
                    statusing.ImportTimesheet(tspUID);

                if (logEvents)
                    appLog.WriteLine(string.Format("STEP 4 - Imported Timesheet: {0}; Resource: {1}; Creator: {2}, TotalActValue: {3}, Period: {4}",
                    tsHeader.Name, tsHeader.ResName,
                    tsHeader.CreatorResName, tsHeader.TotalActValue.ToString("N2"), tspUID.ToString()));


                #endregion

                #region STEP 5 - Submit status update to project manager
                if (submitStatus)
                {
                    timeToProcessStatus.Start();
                    List<Guid> assignmentsUID = StatusingPSI.SubmitStatus(timesheetDS, appLog, logEvents);
                    if (assignmentsUID.Count > 0)
                    {
                        statusing.SubmitStatus(assignmentsUID.ToArray(), "Submitted Status via custom Timesheet event");
                    }
                    if (logEvents)
                        appLog.WriteLine(string.Format("STEP 5 - SubmitStatus for: {0} assignments", assignmentsUID.Count.ToString()));

                    timeToProcessStatus.Stop();
                }
                #endregion

                #region STEP 6 - Handle Exceptions and logging
            }
            catch (SoapException ex)
            {
                exceptionMsg = GeneralHelper.HandleSoapException(ex);
                eventLog.WriteEvent("FAILED to execute custom timesheet event OnUpdated, due to a SoapException: " + exceptionMsg);
            }
            catch (Exception ex)
            {
                exceptionMsg = ex.Message;
                eventLog.WriteEvent("FAILED to execute custom timesheet event OnUpdated, due to an Exception: " + exceptionMsg);
            }
            finally
            {
                if (logEvents)
                {
                    if (exceptionMsg != null)
                    {
                        appLog.WriteLine(string.Format("FAILED to execute custom timesheet event OnUpdated, due to an exception: {0}", exceptionMsg));
                        appLog.WriteLine(string.Format("SubmitStatus: {0}; timesheet URL: {1}; statusing URL: {2}",
                            submitStatus.ToString(), WSUrl(spSite, "timesheet", false), WSUrl(spSite, "statusing", true)));
                        appLog.WriteLine(tsInfo);
                    }
                    else
                        appLog.WriteLine("SUCCESSFULY executed custom timesheet event OnUpdated");

                    appLog.WriteLine(string.Format("Total execution time: {0} seconds; SubmitStatus: {1} seconds\r\n",
                        timeToProcessAll.Elapsed.TotalSeconds.ToString("N"),
                        timeToProcessStatus.Elapsed.TotalSeconds.ToString("N")));

                    appLog.Close();
                }
            }
            #endregion
        }

        #region Private Methods
        private void LoadSettings()
        {
            // Load settings from Microsoft.Office.Project.Server.Eventing.exe.config
            try
            {
                logEvents = bool.Parse(ConfigurationManager.AppSettings.Get("LogTimesheetEvent"));
                logFileEveryHour = bool.Parse(ConfigurationManager.AppSettings.Get("LogFileEveryHour"));
                logFolder = ConfigurationManager.AppSettings.Get("LogFolderPath").Trim();
                sspName = ConfigurationManager.AppSettings.Get("PWASSPName").Trim();
                submitStatus = bool.Parse(ConfigurationManager.AppSettings.Get("AutomaticallySubmitStatusUpdate"));
            }
            catch (Exception e)
            {
                eventLog.WriteEvent(string.Format("Could not load settings from Microsoft.Office.Project.Server.Eventing.exe.config: {0}", e.Message));
            }
        }

        private string WSUrl(SPSite ss, string wsName, bool impersonate)
        {
            string ssHostName = ss.HostName;

            if (hostName.Length > 0) ssHostName = hostName;
            if (impersonate)
                return string.Format("{0}//{1}:56737/{2}/psi/{3}.asmx", ss.Protocol, ssHostName, sspName, wsName);
            else
                return string.Format("{0}/_vti_bin/psi/{1}.asmx", ss.Url, wsName);
        }
        #endregion
    }
}
